package com.cs.framework.jdbc.mybatis3.executor.statement;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.List;

import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.ExecutorException;
import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.session.Configuration;

import com.cs.framework.jdbc.mybatis3.executor.resultset.SelectResultSetHandler;

/**
 * SQL编译处理器
 * 
 * @author hedongzhou
 * 
 */
public class SelectStatementHandler {

	protected Configuration configuration;

	protected SelectResultSetHandler resultSetHandler;

	protected ParameterHandler parameterHandler;

	protected MappedStatement mappedStatement;

	protected BoundSql boundSql;

	public SelectStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameter,
			BoundSql boundSql) {
		this.configuration = mappedStatement.getConfiguration();
		this.mappedStatement = mappedStatement;
		this.boundSql = boundSql;
		this.parameterHandler = configuration.newParameterHandler(mappedStatement, parameter, boundSql);
		this.resultSetHandler = new SelectResultSetHandler(mappedStatement);
	}

	/**
	 * 查询
	 * 
	 * @throws ClassNotFoundException
	 */
	public <T> List<T> query(Statement statement, Class<T> resultType) throws SQLException, ClassNotFoundException {
		PreparedStatement ps = (PreparedStatement) statement;
		ps.execute();
		return resultSetHandler.<T> handleResultSets(ps, resultType);
	}

	protected Statement instantiateStatement(Connection connection) throws SQLException {
		String sql = boundSql.getSql();
		return connection.prepareStatement(sql);
	}

	public void parameterize(Statement statement) throws SQLException {
		parameterHandler.setParameters((PreparedStatement) statement);
	}

	public Statement prepare(Connection connection) throws SQLException {
		ErrorContext.instance().sql(boundSql.getSql());
		Statement statement = null;
		try {
			statement = instantiateStatement(connection);
			setStatementTimeout(statement);
			setFetchSize(statement);
			return statement;
		} catch (SQLException e) {
			closeStatement(statement);
			throw e;
		} catch (Exception e) {
			closeStatement(statement);
			throw new ExecutorException("Error preparing statement.  Cause: " + e, e);
		}
	}

	protected void setStatementTimeout(Statement stmt) throws SQLException {
		Integer timeout = mappedStatement.getTimeout();
		Integer defaultTimeout = configuration.getDefaultStatementTimeout();
		if (timeout != null) {
			stmt.setQueryTimeout(timeout);
		} else if (defaultTimeout != null) {
			stmt.setQueryTimeout(defaultTimeout);
		}
	}

	protected void setFetchSize(Statement stmt) throws SQLException {
		Integer fetchSize = mappedStatement.getFetchSize();
		if (fetchSize != null) {
			stmt.setFetchSize(fetchSize);
		}
	}

	protected void closeStatement(Statement statement) {
		try {
			if (statement != null) {
				statement.close();
			}
		} catch (SQLException e) {
		}
	}

}
